package cn.kgc;

import cn.kgc.grace.result.GraceJSONResult;
import cn.kgc.grace.result.ResponseStatusEnum;
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashSet;
import java.util.Set;


/**
 * @author k
 */
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
        //初始化规则进行限流
        initGatewayRules();
        //如果超出限流规则返回错误信息
        initBlockHandler();
    }

    /**
     * 返回限流的信息
     */
    private static void initBlockHandler() {

        GatewayCallbackManager.setBlockHandler((exchange, t) -> {
            //返回给前段的限流信息
            GraceJSONResult graceJSONResult
                    = GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR_ZUUL);
            //返回响应的信息
            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(Mono.just(graceJSONResult), GraceJSONResult.class);
        });
    }

    /**
     * 给不同的用户请求分组设置不同的流控规则
     */
    private  static  void initGatewayRules(){
        //将用户的请求进行分组
        initCustomizedApis();
        //给分组设置流控规则 GatewayFlowRule  网关流控规则
        Set<GatewayFlowRule> rules = new HashSet<>();
        //给resume-api分组定义网关的流控规则
        GatewayFlowRule resumeRule = new GatewayFlowRule("resume-api");
        resumeRule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
        //以resume开头的请求最多10个请求
        resumeRule.setCount(10);
        //1秒 （每秒）
        resumeRule.setIntervalSec(1);
        //以QPS
        resumeRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //流控效果
        resumeRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        //设置流控效果为快速失败
        resumeRule.setBurst(0);
        //将流控规则添加到集合中
        rules.add(resumeRule);
        //将规则保存到Sentinel
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 根据用户的请求路径不同  将用户请求分组
     */

    private static void initCustomizedApis() {

        //Set集合存储的是Api接口的定义信息
        Set<ApiDefinition> definitions = new HashSet<>();

        //存放的是ApiPredicateItem对象 api接口路由对象
        HashSet<ApiPredicateItem> apiPredicateItems = new HashSet<>();

        //定义了一个resumePredicateItem的路由规则 （如果用户访问的地址是resume开头的，那么就匹配这个规则）
        ApiPathPredicateItem resume = new ApiPathPredicateItem();
        resume.setPattern("/resume/**");
        resume.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX);
        apiPredicateItems.add(resume);
        //将以resume开头的请求路由到resume-api服务 将分组的信息保存到definitions中
        definitions
                .add(new ApiDefinition("resume-api")
                        .setPredicateItems(apiPredicateItems));

        //将分组信息保存到Sentinel中
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}
